﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace tproto.Maker
{
    public class TprotoContext
    {
        private readonly Dictionary<string, TprotoType> Types = new Dictionary<string, TprotoType>
        {
            { "boolean", new TprotoType("boolean", "Boolean", 1)},
            { "char",  new TprotoType("char", "Char", 1)},
            { "byte", new TprotoType("byte", "Byte", 1)},
            { "short", new TprotoType("short", "Short", 2)},
            { "int", new TprotoType("int", "Int32", 4)},
            { "long", new TprotoType("long", "Long", 8)},
            { "float", new TprotoType("float", "Float", 4)},
            { "double", new TprotoType("double", "Double", 8)},
        };


        public TprotoType GetType(string type)
        {
            return Types.ContainsKey(type) ? Types[type] : null;
        }

        public TprotoType GetOrCreateType(string type)
        {
            TprotoType nowType = GetType(type);
            if (nowType != null) { return nowType; }
            // create type
            TprotoType ntype = new TprotoType(type, type, 0, false);
            Types[type] = ntype;
            return ntype;
        }

        public void SetTypeBaseName(string type, string name)
        {
            TprotoType baseType = GetType(type);
            if (baseType == null) { return; }
            baseType.Name = name;
        }
        public void SetTypeObjectName(string type, string name)
        {
            TprotoType baseType = GetType(type);
            if (baseType == null) { return; }
            baseType.ObjectName = name;
        }

    }

    public class TprotoFile
    {
        public string Package { get; set; } // 包名
        public List<string> Imports { get; } // 导入
        public List<TprotoProto> Protos { get; }

        public TprotoContext Context { get; }

        public TprotoFile(TprotoContext context)
        {
            Context = context;
            Imports = new List<string>();
            Protos = new List<TprotoProto>();
        }



    }

    /// <summary>
    /// 协议体结构
    /// </summary>
    public class TprotoProto
    {
        /// <summary>
        /// 协议名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 协议变量
        /// </summary>
        public List<TprotoAttribute> Attributes { get; }

        public TprotoContext Context { get { return File.Context; } }

        /// <summary>
        /// 协议文件
        /// </summary>
        public TprotoFile File { get; }

        public TprotoProto(TprotoFile file)
        {
            File = file;
            Attributes = new List<TprotoAttribute>();
        }

        public int GetBaseTypeSize()
        {
            int baseTypeSize = 0;
            foreach (var attribute in Attributes)
            {
                baseTypeSize += attribute.Type.BaseTypeSize;
            }
            return baseTypeSize;
        }

    }

    public class TprotoAttribute
    {
        public TprotoProto Proto { get; }
        public TprotoContext Context { get { return Proto.Context; } }

        public TprotoAttributeType Type { get; set; }
        public string Name { get; set; }
        public string Tag { get; set; }
        public bool HasTag { get { return !string.IsNullOrEmpty(Tag); } }

        public TprotoAttribute(TprotoProto proto)
        {
            Proto = proto;
        }

    }


    public class TprotoAttributeType
    {
        public TprotoAttribute Attribute { get; }
        public TprotoContext Context { get { return Attribute.Context; } }

        public string Name { get { return type.Name; } }
        public string ObjectName { get { return type.ObjectName; } }
        public bool IsBaseType { get { return type.IsBaseType; } }
        public bool IsArray { get { return isArray; } }
        public int BaseTypeSize { get { return GetBaseTypeSize(); } }

        private TprotoType type;
        private bool isArray;

        private TprotoAttributeType(TprotoAttribute attribute, TprotoType type, bool isArray)
        {
            Attribute = attribute;
            this.type = type;
            this.isArray = isArray;
        }

        /// <summary>
        /// 计算基础长度(如果是非基础类型不算入, 数组也不算入)
        /// </summary>
        /// <returns></returns>
        protected int GetBaseTypeSize()
        {
            if (isArray) { return 0; }
            return type.TypeSize;
        }

        public static TprotoAttributeType CreateTprotoAttributeType(TprotoAttribute attribute, string typeStr)
        {
            bool isArray = (typeStr.LastIndexOf('[') == typeStr.Length - 2) && (typeStr.IndexOf(']') == typeStr.Length - 1);
            if (isArray)
            {
                typeStr = typeStr.Substring(0, typeStr.Length - 2);
            }
            // 获取是否是基础类型
            TprotoType type = attribute.Context.GetOrCreateType(typeStr);

            // 创建参数
            TprotoAttributeType atype = new TprotoAttributeType(attribute, type, isArray);
            return atype;
        }

        public bool IsStringType { get { return Name.Equals("String"); } }

    }

    public class TprotoType
    {
        public string Name { get; set; }
        public string ObjectName { get; set; }
        public int TypeSize { get; }
        public bool IsBaseType { get; }

        public TprotoType(string name, string objectName, int typeSize, bool isBaseType = true)
        {
            Name = name;
            ObjectName = objectName;
            TypeSize = typeSize;
            IsBaseType = isBaseType;
        }
    }
}
